// ===========================
// lo: LoNote 函式庫
//      賦與 LoNote 頁面各種功能
//
//      本函式庫需依賴以下套件，使用時請一併匯入，否則部份功能可能有所缺失：
//        - jquery
//        - jquery-ui
//        - jquery-ui css file
//        - URI.js
//        - shortcut.js
// ===========================




(function() {

    window['lo'] = {}


    // ==============================
    // sel: 選擇區域操作函式
    // ==============================

    window['lo']['sel'] = {};

    //取得當前游標選擇區域並回傳
    function saveSelection() {
        if (window.getSelection) {
            sel = window.getSelection();
            if (sel.getRangeAt && sel.rangeCount) {
                return sel.getRangeAt(0);
            }
        } else if (document.selection && document.selection.createRange) {
            return document.selection.createRange();
        }
        return null;
    };



    //指定一個由saveSelection設定的選擇區域，恢復該選擇區域
    function restoreSelection(range) {
        if (range) {
            if (window.getSelection) {
                sel = window.getSelection();
                sel.removeAllRanges();
                sel.addRange(range);
            } else if (document.selection && range.select) {
                range.select();
            }
        }
    };



    // 將 html 插入游標位置，以取代 execCommand insertHTML
    // undo 功能會失效，不能用！
    // function pasteHtmlAtCaret(html) {
    //     var sel, range;
    //     sel = window.getSelection();
    //     if (sel.getRangeAt && sel.rangeCount) {
    //         range = sel.getRangeAt(0);
    //         range.deleteContents();

    //         // Range.createContextualFragment() would be useful here but is
    //         // non-standard and not supported in all browsers (IE9, for one)
    //         var el = document.createElement("div");
    //         el.innerHTML = html;
    //         var frag = document.createDocumentFragment(), node, lastNode;
    //         while ( (node = el.firstChild) ) {
    //             lastNode = frag.appendChild(node);
    //         }
    //         range.insertNode(frag);

    //         // Preserve the selection
    //         if (lastNode) {
    //             range = range.cloneRange();
    //             range.setStartAfter(lastNode);
    //             range.collapse(true);
    //             sel.removeAllRanges();
    //             sel.addRange(range);
    //         }
    //     }
    // };


    window['lo']['sel']['save'] = saveSelection;
    window['lo']['sel']['restore'] = restoreSelection;






    // ==============================
    // scr: scroll 操作函式
    // ==============================

    window['lo']['scr'] = {};

    //記錄scroll
    function save_scroll(){
        localStorage['scrollY_of_' + location.pathname] = $(window).scrollTop();
    };



    //恢復scroll
    function restore_scroll(){
        var old_scrollY = localStorage['scrollY_of_' + location.pathname];
        old_scrollY = parseInt(old_scrollY, 10);
        $(window).scrollTop(old_scrollY); //嘗試滾動
        //滾動不到位的話，完全載入後再滾動一次
        if($(window).scrollTop() < old_scrollY){
            $(window).one('load', restore_scroll);
        }
    };



    window['lo']['scr']['save'] = save_scroll;
    window['lo']['scr']['restore'] = restore_scroll;






    // ==============================
    // util: 雜項功能
    // ==============================

    window['util'] = {}

    // 回傳標準滑鼠滾動事件的滾動格數，fx chrome 通用
    // 上滾為正值
    function get_mouse_scroll(e){
        var scroll = 0; //滾動格數，滾輪往上滾為正值
        if (e.wheelDelta){ //chrome
            scroll = (e.wheelDelta/120);
        } else if (e.detail){//fx
            scroll = e.detail/3 * -1;
        }
        return scroll;
    };



    //阻止事件傳播
    function stopEvent(e){
        if(e.preventDefault)
            e.preventDefault(); //fx阻止事件傳播
        if(e.stopPropagation)
            e.stopPropagation(); //fx阻止事件傳播
        return false;
    };



    // ajax 標準錯誤處理
    function ajax_standard_error(jqXHR, errortitle){
        if (jqXHR.status == 0){
            $('<div>')
                .attr('title', errortitle)
                .append('Server offline').
                dialog({dialogClass: 'ui-state-error '});
        } else {
            $('<div>')
                .attr('title', errortitle)
                .append(jqXHR.responseText).
                dialog({dialogClass: 'ui-state-error '});
        }
    }






    // ==============================
    // 圖片操作支援
    //     必須要掛在 img 的 mousewheel 和 click 上才能工作
    // ==============================

    // 用defaultwidth恢復圖片大小
    function setDefaultSize(e){
        if(e.shiftKey){
            $(this).removeAttr('height'); // 清掉 height 屬性
            if (this.hasAttribute("defaultwidth")){
                this.width = parseInt(this.getAttribute("defaultwidth"), 10);
            }
            //向前相容用
            else if(this.style.zoom != 100){
                this.style.zoom = "100%";
            }
            content_change(); // 說明頁面變更
            return stopEvent(e)
        }
    }



    // 用滾輪縮放圖片
    function mouseWheelResize(e){
        if (e.shiftKey){
            var scroll = get_mouse_scroll(e)

            //defaultwidth是我自己加入的屬性
            if (this.hasAttribute("defaultwidth")){
                //放大量為每格10%的defaultwidth
                var width = this.width;
                // scale == 每格縮放大小，為 defaultwidth 的 1/10
                var scale = parseInt(this.getAttribute("defaultwidth"), 10)/10;
                width += scroll * scale;
                if (width > 0){
                    this.width = width;
                }
                content_change(); // 說明頁面變更
            }
            //如果沒有defaultwidth，則從當前的width中取得defaultwidth的值
            else if(this.hasAttribute("width")){
                this.setAttribute("defaultwidth", this.width);
            }
            //如果連width也沒有，則分析來源判斷檔案的width
            else{
                this.setAttribute("width", this.width);
            }
            return stopEvent(e);
        }
    }






    // ==============================
    // 搜尋操作支援
    // ==============================

    //叫出搜索框
    function prompt_search(){
        var sedia = $('#lo_search_dialog');
        if (sedia.length > 0){
            sedia.dialog({
                title: 'Search',
                width: 400,
                position: {my: 'right top', at: 'right top', of: window },
            });
        } else {
            // 如果沒有找到現有的對話框，就自制一個之後嵌入，並bind上相應功能
            var sedia_str = '<!-- Hidden Search Dialog --> <div id="lo_search_dialog" style="display: none;"> <input id="lo_search_input" type="search" x-webkit-speech /><input id="lo_search_btn" value="Search" type="button" /> <div id="lo_search_result"></div> </div> ';
            $('body').append(sedia_str);
            $('#lo_search_input').bind('keydown', function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); get_search($(this).val()); return false;}});
            $('#lo_search_btn').on('click', function(e) { e.preventDefault(); get_search($('#lo_search_input').val()); return false;});
            //$('#lo_search_input').bind('input', function(e) { get_search($(this).val()); return false;});
            // 重新遞迴叫出
            prompt_search();
        }
    };


    
    // 送出關鍵字進行搜尋，並將結果嵌入預設的搜索結果位置
    function get_search(key){
        var start = function(){
            $('#lo_search_result').html('waiting...');
            return true;
        };
        var success = function(data){
            $('#lo_search_result').html(data);
            return false;
        };
        function error(jqXHR){return ajax_standard_error(jqXHR, 'Search Failed');};
        var d = {
            cmd: 'search',
            key: key
        };
        $.ajax({
            url: '/search/',
            type: 'GET',
            data: d,
            async: true,
            beforeSend: start,
            success: success,
            error: error,
        });
    };






    // ==============================
    // noteop: 提供筆記頁 Meta 操作的支援
    //     包括更名、變更位置、刪除、新建……等功能
    //
    //     這些函式會接受當前頁面的 location 而改變運作方式，
    //     故只有在一般筆記頁 (/site/<buuid>/<uuid>/) 下才能正確運作。
    // ==============================

    window['lo']['noteop'] = {};

    // 叫出可以變更筆記位置的對話視窗
    function prompt_position_update(){
        function ok(){
            var parent = $(this).find('#parent').val();
            var index = $(this).find('#position').val();
            $(this).dialog('close');
            position_update(parent, index);
            $(this).dialog('destroy');
            return false;
        };

        var diaopt = {
            minWidth: '400px',
            modal: true,
            width: 400,
            title: 'Move current page to ...',
            buttons: {
                "OK": ok,
                "Cancel": function(){$(this).dialog( "destroy" )}}
        };
        var dia = $('<div><p id="info"></p>position:<br/><input id="position" type="text" style="width:100%;" placeholder="BLANK for bottom, 1 for top, +3 for lower 3 rows."></input><br/><br/>new parent\'s URL or UUID:<br/><input id="parent" type="text" style="width:100%;" placeholder="BLANK represents not need to change."></input></div>')
        dia.bind('keydown', function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});
        dia.dialog(diaopt);
        return false;
    };



    // 變更筆記位置
    function position_update(parent, index){
        function success(data){ location.reload(); };

        function error(jqXHR){return ajax_standard_error(jqXHR, 'Position Update Failed');};

        var d = {
            cmd: 'position_update',
            parent_url: parent,
            index: index
        };
        $.ajax({
            type: 'PUT',
            data: d,
            async: false,
            success: success,
            error: error,
        });
    };


    
    // 叫出確認是否要刪除當前筆記的視窗
    function confirm_note_delete(){
        function ok(){
            $(this).dialog('close');
            note_delete();
            $(this).dialog('destroy');
            return false;
        };

        var diaopt = {
            modal: true,
            width: 400,
            title: 'Delete note',
            buttons: {
                "OK": ok,
                "Cancel": function(){$(this).dialog( "destroy" )}}
        };
        var dia = $('<div><p>Delete the current page?</p><p>(include all sub-page, resource and history records)</p></div>')
        dia.bind('keydown', function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});
        dia.dialog(diaopt);
        return false;
    };



    // 刪除當前的筆記
    function note_delete(){
        function success(data){ location.href = data; };
        function error(jqXHR){return ajax_standard_error(jqXHR, 'Delete Failed');};

        var d = {
            cmd: 'note_delete',
        };
        $.ajax({
            type: 'DELETE',
            data: d,
            async: false,
            success: success,
            error: error,
        });
    }



    // 叫出對話框，確認是否要刪除一筆歷史記錄
    function confirm_record_delete(record_name, comment){
        var ok = function (){
            $(this).dialog('close');
            record_delete(record_name);
            $(this).dialog('destroy');
            return false;
        }
        var diaopt = {
            width: 400,
            modal: true,
            title: 'Delete the record?',
            buttons: {
                "OK": ok,
                "Cancel": function(){$(this).dialog( "destroy" )}}};
        var dia = $('<div><p>delete the record:</p><div class="ui-state-highlight ui-corner-all" style="padding:6px;margin-left:20px;">' + comment + '</div><p style="font-size:smaller;text-align:right;">(' + record_name + ')</p></div>');
        dia.bind('keydown',function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});
        dia.dialog(diaopt);
        return false;
    };



    // 刪除一筆歷史記錄
    function record_delete(record_name){
        function success(data){
            $('[id="'+record_name+'"]').hide();
            return false;
        };
        function error(jqXHR){return ajax_standard_error(jqXHR, 'Resource Delete Failed');};

        var d = {
            cmd: 'record_delete',
            record_name: record_name
        };
        $.ajax({
            type: 'DELETE',
            data: d,
            async: true,
            success: success,
            error: error,
        });
        return false;
    };



    // 叫出對話框，確認刪除指定的資源
    function confirm_resource_delete(res_name){
        var ok = function (){
            $(this).dialog('close');
            resource_delete(res_name);
            $(this).dialog('destroy');
            return false;
        }
        var diaopt = {
            width: 400,
            modal: true,
            title: 'Delete the Resource?',
            buttons: {
                "OK": ok,
                "Cancel": function(){$(this).dialog( "destroy" )}}};
        var dia = $('<div><p>delete the Resource:</p><div class="ui-state-highlight ui-corner-all" style="padding:6px;margin-left:20px;"><a href="' + res_name + '">' + res_name + '</a></div></div>');
        dia.bind('keydown',function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});
        dia.dialog(diaopt);
        return false;
    };



    // 刪除指定的資源
    function resource_delete(res_name){
        function success(data){
            $('[res_name="'+res_name+'"]').hide();
            return false;
        };
        function error(jqXHR){return ajax_standard_error(jqXHR, 'Resource Delete Failed');};

        var d = {
            cmd: 'resource_delete',
        };
        $.ajax({
            url: res_name,
            type: 'DELETE',
            data: d,
            async: true,
            success: success,
            error: error,
        });
        return false;
    };



    // 叫出對話窗，讓使用者新增一頁
    function prompt_note_create(){
        function ok(){
            var title = $(this).find('#title').val();
            if (!title){
                $(this).find('#info').html('Title must be set!').css('color','red');
                return false;
            }
            $(this).dialog('close');
            var position = $(this).find('#position').val();
            note_create(position, title);
            $(this).dialog('destroy');
            return false;
        };

        var diaopt = {
            modal: true,
            width: 400,
            title: 'Create a new note',
            buttons: {
                "OK": ok,
                "Cancel": function(){$(this).dialog( "destroy" )}}
        };
        var dia = $('<div><p id="info"></p>Title:<br/><input id="title" type="text" style="width:100%;" placeholder="Please input your new note name"></input><br/><br/>New Note Position:<br/><select id="position"><option value="before">⇧ before</option><option value="sub">⇨ sub</option><option value="next" selected="selected">⇩ next</option></select></div>')
        dia.bind('keydown', function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});
        dia.dialog(diaopt);
        return false;
    };



    // 新增一頁
    // position須為："next" = 下一個；"before" = 上一個；"sub" = 下層的尾巴
    function note_create(position, new_title){
        function success(data){ location.href = data; };
        function error(jqXHR){return ajax_standard_error(jqXHR, 'Note Create Failed');};

        var d = {
            cmd: 'note_create',
            position: position,
            new_title: new_title,
        };
        $.ajax({
            type: 'POST',
            data: d,
            async: false,
            success: success,
            error: error,
        });
    };



    // 叫出更新筆記名稱的視窗
    function prompt_title_update(){
        function ok(){
            var new_title = $(this).find('#new_title').val();
            if (!new_title){
                $(this).find('#info').html('New title cannot be blank!').css('color','red');
                return false;
            }
            $(this).dialog('close');
            title_update(new_title);
            $(this).dialog('destroy');
            return false;
        };

        var diaopt = {
            modal: true,
            width: 400,
            title: 'Change note title',
            buttons: {
                "OK": ok,
                "Cancel": function(){$(this).dialog( "destroy" )}}
        };
        var dia = $('<div><p id="info"></p>new title:<br/><input id="new_title" type="text" style="width:100%;" placeholder="Please input new title" value="'+ $('meta[name="description"]').attr('content') +'"></input></div>')
        dia.bind('keydown', function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});
        dia.dialog(diaopt);
        return false;
    };



    // 更動筆記名稱
    function title_update(new_title){
        function success(data){ location.reload(); };
        function error(jqXHR){return ajax_standard_error(jqXHR, 'Title Update Failed');};

        var d = {
            cmd: 'title_update',
            new_title: new_title,
        };
        $.ajax({
            type: 'PUT',
            data: d,
            async: false,
            success: success,
            error: error,
        });
    };



    // 叫出匯出視窗
    function prompt_note_export(){
        function ok(){
            var fname = $(this).find('#fname').val();
            if (!fname){
                $(this).find('#info').html('Filename cannot be blank!').css('color','red');
                return false;
            }
            $(this).dialog('close');
            note_export(fname);
            $(this).dialog('destroy');
            return false;
        };

        var diaopt = {
            modal: true,
            width: 400,
            title: 'Export as a new notebook',
            buttons: {
                "OK": ok,
                "Cancel": function(){$(this).dialog( "destroy" )}}
        };
        var dia = $('<div>')
            .append('<p id="info"></p>Export Filename:<br/><input id="fname" type="text" style="width:100%;" placeholder="Please input a new filename"></input>')
            .bind('keydown', function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});
        dia.find('#fname').val($('meta[name="description"]').attr('content'))
        dia.dialog(diaopt);
        return false;
    };



    // 匯出當前頁面成為新筆記
    function note_export(fname){
        function success(data){
            $('<div>').attr('title', 'Export Success!')
                .append('<p style="font-size:small;">If you want to reload or download it, go to <a href="/config" style="font-size:large;">Configure Page</a> right now!</p>')
                .dialog()
        };
        function error(jqXHR){return ajax_standard_error(jqXHR, 'Note Export Failed');};

        var d = {
            cmd: 'note_export',
            fname: fname,
        };
        $.ajax({
            type: 'POST',
            data: d,
            async: false,
            success: success,
            error: error,
        });
    };



    // 叫出匯入視窗
    function prompt_book_import(){
        function ok(){
            var fname = $(this).find('#fname').val();
            if (!fname){
                $(this).find('#info').html('Filename cannot be blank!').css('color','red');
                return false;
            }
            $(this).dialog('close');
            book_import(fname);
            $(this).dialog('destroy');
            return false;
        };

        var diaopt = {
            modal: true,
            width: 400,
            title: 'Import a notebook as a sub-page',
            buttons: {
                "OK": ok,
                "Cancel": function(){$(this).dialog( "destroy" )}}
        };
        var dia = $('<div>')
            .append('<p id="info"></p>Import Filename:<br/><input id="fname" type="text" style="width:100%;" placeholder="Please input a existing book filename"><p style="font-size: small;">(You can find all filename in the <a href="/config" target="_blank">Configure Page</a>)</p><p style="font-size: small;">Hint: In most time, you can use "move" to replace "import".</p></input>')
            .bind('keydown', function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});
        dia.dialog(diaopt);
        return false;
    };



    // 匯入一本筆記本
    function book_import(fname){
        function success(data){
            location.reload();
        };
        function error(jqXHR){return ajax_standard_error(jqXHR, 'Note Export Failed');};

        var d = {
            cmd: 'book_import',
            fname: fname,
        };
        $.ajax({
            type: 'POST',
            data: d,
            async: false,
            success: success,
            error: error,
        });
    };


    window['lo']['noteop']['pos_dia'] = prompt_position_update;
    window['lo']['noteop']['del_dia'] = confirm_note_delete;
    window['lo']['noteop']['record_del_dia'] = confirm_record_delete;
    window['lo']['noteop']['resource_del_dia'] = confirm_resource_delete;
    window['lo']['noteop']['create_dia'] = prompt_note_create;
    window['lo']['noteop']['rename_dia'] = prompt_title_update;
    window['lo']['noteop']['export_dia'] = prompt_note_export;
    window['lo']['noteop']['import_dia'] = prompt_book_import;






    // ==============================
    // content: 提供筆記頁中 Content 相關的操作支援
    //     包括儲存、自動儲存、建立新版本……等功能
    //
    //     這些函式會……
    //       1. 接受當前頁面的 location 而改變運作方式，
    //          故只有在一般筆記頁 (/site/<buuid>/<uuid>/) 下才能正確運作。
    //       2. 頁面需提供以下三個 DOM 物件：
    //              article#main_content
    //              time#modified_time
    //              time#frozen_time
    //          
    //          若頁面結構中不含這些物件，函式功能將會失效。
    // ==============================

    window['lo']['content'] = {};

    var autoSaveMode = true; // 用var來宣告私有變數
    var alreadySave = true;
    var changed_when_saving = false;
    var internal = null;     // setInternal回傳來的物件
    var saveLock = false;    // 儲存時的非同步鎖（這不完美，但差不多夠用就好……）

    // 將本地 article#main_content 內的資料更新到伺服器
    function content_update(async){   //用this來宣告public方法&變數

        function parse_time(timestamp){
            // 將伺服器標準時間戳轉為javascript Date格式
            var part = timestamp.match(/(\d+)/g);
            var d = new Date();
            d.setUTCFullYear(part[0]);
            d.setUTCMonth(part[1] - 1);
            d.setUTCDate(part[2]);
            d.setUTCHours(part[3]);
            d.setUTCMinutes(part[4]);
            d.setUTCSeconds(part[5]);
            var stamp_tz_leading_hours = parseInt(part[7], 10) / 100;
            var stamp_tz_leading_seconds = stamp_tz_leading_hours*60*60;
            var ans = new Date(d - stamp_tz_leading_seconds*1000);
            return ans
        };

        function update_complete(){
            saveLock = false;
            //是否在儲存過程中內容又有改變
            if (changed_when_saving) {
                content_change();
                changed_when_saving = false;
                return content_update(async);
            }
        };

        function update_success(data){
            //不管原本是什麼，將 pubdate 改到 mtime，更新mtime.datetime
            $('#modified_time').attr({'datetime': data.mtime_stamp, 'pubdate': ''});
            $('#frozen_time').attr('datetime', '').removeAttr('pubdate');
            //更改 CurrentVersionTime 上的時間戳
            //var msec = Date.parse(data.mtime_stamp);
            //var d = new Date(msec);
            //var d = parse_time(data.mtime_stamp)
            var part = data.mtime_stamp.match(/(\d+)/g);
            //Parsing 這個實在是要讓人昏倒……、
            var cvt_stamp = part[0]
                + '-' + part[1]
                + '-' + part[2]
                + ' ' + part[3]
                + ':' + part[4]
                + ':' + part[5];
            //var cvt_stamp = d.getFullYear()
            //    + '-' + (d.getMonth() + 1)
            //    + '-' + d.getDate()
            //    + ' ' + d.getHours()
            //    + ':' + d.getMinutes()
            //    + ':' + d.getSeconds();
            $('#current_version_time').html(cvt_stamp);
            $('#current_version > a').attr('title', 'Current Version: ' + cvt_stamp);
            //改變target
            $('#current_version').find('.target').removeClass('target');
            $('#current_version > a').addClass('target');
            //設定儲存旗標
            alreadySave = true;
            //成功最晚提示
            $('#btn_content_update .response_notify:first')
                .html('OK ✔').css("color","green");
            $('#btn_content_update')
                .attr('title', 'Save Status: Save OK ✔');
        };

        function update_error(jqXHR){
            if (jqXHR.status == 0){
                autoSaveMode = true; //伺服器離線，則不斷重試
                $('#btn_content_update .response_notify:first')
                    .html('off-line ✘').css("color","red");
                $('#btn_content_update')
                    .attr('title', 'Save Status: Off-line ✘');
            } else if(jqXHR.status == 403){
                autoSaveMode = false; //編輯衝突失敗後，不要自動重試
                $('#btn_content_update .response_notify:first')
                    .html('edit conflicts ✘').css("color","red");
                $('#btn_content_update')
                    .attr('title', 'Save Status: Edit conflicts ✘');
                $('<div>').attr('title', 'Edit confilct!')
                    .append('<p>Sorry, Other client already modified this page.</p>')
                    .append('<p style="font-size: smaller;">Please reload for newest content then re-edit your data.</p>')
                    .dialog({'dialogClass': 'ui-state-error'})
            } else {
                $('#btn_content_update .response_notify:first')
                    .html('Error ✘').css("color","red");
                $('#btn_content_update')
                    .attr('title', 'Save Status: Unidentified Error ✘');
            }
        };

        // 前面是內部函式定義，從這行開始實際執行
        if(alreadySave){         //不加任何前綴來使用private
            $('#btn_content_update .response_notify:first')
                .html('not need ✘').css('color', 'yellow');
            $('#btn_content_update')
                .attr('title', 'Save Status: Not need to save ✘');
            return false;
        }

        var d = {
            cmd: 'content_update',
            modified_time: $('#modified_time').attr('datetime'),
            frozen_time: $('#frozen_time').attr('datetime'),
            content: $('#main_content').html()
        };

        // 檢查鎖與取鎖
        if(saveLock){
            return false;
        }else{
            saveLock = true;
        }
        $.ajax({
            type: 'PUT',
            data: d,
            dataType: 'json',
            async: async,
            success: update_success, //不加前綴使用private
            error: update_error,
            complete: update_complete
        });
        return false;
    };



    // 跳出對話框讓使用者輸入新版本的註解，確認後將新的記錄版本塞給伺服器
    function prompt_make_record() {
        var ok = function (){
            $(this).dialog('close');
            var comment = $(this).find('input#comment').val();
            make_record(comment);
            $(this).dialog('destroy');
            return false;
        }
        var diaopt = {
            modal: true,
            title: 'Make A Record',
            width: 400,
            buttons: {
                "OK": ok,
                "Cancel": function(){$(this).dialog( "destroy" )}}};
            dia = $('<form>Comment:<br/><input id="comment" style="width: 100%;" placeholder="Describe what this version are."/></form>');
        //支援使用者敲擊enter
        dia.bind('keydown',function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});
        dia.dialog(diaopt);
        return false;
    };



    // 將本地 article#main_content 內的資料設為一個新版本，之後更新到伺服器
    function make_record(comment){
        function success(data){
            $('<div>').attr('title', 'Make Record Success')
                .append('<p>Save OK!</p>')
                //.append('<p style="font-size: smaller">The record list no update yet, if you want to check, please reload this page.</p>')
                .dialog();
        }

        function error(jqXHR){ajax_standard_error(jqXHR, 'Make Record Failed');}

        if(comment == '') {
            $('<div title="Error">You must input the record\'s comment.</div>').dialog({dialogClass: 'ui-state-error '});
            return false;
        }
        var d = {
            cmd: 'make_record',
            comment: comment,
            content: $('#main_content').html()
        };
        $.ajax({
            type: 'POST',
            data: d,
            async: true,
            success: success,
            error: error
        });
        return false;
    }



    // 檢查這頁是否為版本記錄頁（用 #frozen_time 的內容來判斷）
    function is_record_page() {
        if ($('#frozen_time').attr('datetime'))
        {return true;}
        else
        {return false;}
    };



    // 執行此函式會將 content 標註為已改變，以支援儲存功能正常運作
    function content_change(){
        if(saveLock){
            changed_when_saving = true;
        } else {
            alreadySave = false;
            $('#btn_content_update .response_notify:first')
                .html('changed ≠').css('color','#222');
            $('#btn_content_update')
                .attr('title', 'Save Status: Changed ≠');
        }
        return false;
    };



    // 檢查並掛載所有 content 必要功能
    function content_startup(){
        // 在有必要的情況下，將 content 內容推到服務器
        // 本函式由計時器負責執行
        function content_auto_update(async){
            if(autoSaveMode && !alreadySave && !is_record_page())
                content_update(async);
        };

        // 本函式指定 content 頁面結束前的處理動作
        function before_unload(){

            // 定義離開頁面時，將 content 推到服務器的方法...
            function content_exit_update(){
                content_auto_update(false);
                if(!alreadySave){
                    // 沒有儲存的狀態
                    if (is_record_page())
                        $('<div>').attr('title', 'RECORD PAGE Modified Not Save!')
                            .append('<p>This is a <b>RECORD PAGE</b> and your not save it yet!</p>')
                            .append('<p style="font-size:smaller;">For your <b>ORIGINAL CURRENT VERSION</b> safety, LoNote will not make auto-save for this page changed.</p>')
                            .append('<p style="font-size:smaller;">if you want to overwrite the <a href="." target="_blank">original current version</a> by this one, please click the <b>SAVE button</b> manually.</p>')
                            .dialog({'dialogClass': 'ui-state-error','width': 400});
                    else{
                        // 注意，這邊也有可能是其他線程存到一半後跳出的狀況。
                        $('<div title="Your Page Not Save!"><p>For some reason, LoNote not save this page automatically.</p><p style="font-size: smaller;">If you want keep your edit, try to click the save button on left-side panel.</p></div>')
                            .dialog({'dialogClass': 'ui-state-error'});
                    }
                    return 'Page not save! really exit?';
                }
            };
            return content_exit_update();
        };

        // 前面是函式定義，從這行正式開始
        // 如果條件不滿足，放棄初始化
        //
        if ($('article#main_content').length == 0) {
            return false;
        }

        document.getElementById('main_content')
            .addEventListener('input', content_change, false);

        // var observer = new MutationObserver(function(mutations) {
        //     mutations.forEach(function(mutation) {
        //         alert(mutation.type);
        //     });    
        // });
        // var config = {subtree: true, childList: true, characterData: true }
        // observer.observe(document.getElementById('main_content'), config);

        // 對 opera 的簡易支持。opera(12.12)不支援 input event 也不支援 MutationObserver
        // document.getElementById('main_content')
        //     .addEventListener('keydown', content_change, false);

        internal = window.setInterval(
                function() { content_auto_update(true);}, 15000);

        $(window).on('beforeunload',
                function (ev){
                    return before_unload();
                });
    };



    // 提供滾動到標題的功能
    function show_heading_list(){
        // 用資料生成顯示
        function gen_heading_list(heading_list_array){
            // 變化等級並插入
            function indent(index, target_level, innerHTML, current_level, jqElem){
                var need_in = target_level - current_level;
                if (need_in == 0){
                    function get_func(index){
                        return function(){
                            aside.dialog('close')
                            scroll_to_heading(index);
                        }
                    }
                    var item = $('<li>').html(innerHTML);
                    item.addClass('heading_item')
                        .addClass('heading_level_'+target_level)
                        .bind('click', get_func(index));

                    jqElem.append(item);
                    return jqElem
                } else if (need_in > 0){
                    var new_jqElem = $('<ol>');
                    jqElem.append(new_jqElem);
                    return indent(index, target_level, innerHTML, current_level + 1, new_jqElem);
                } else {
                    var new_jqElem = jqElem.parent();
                    return indent(index, target_level, innerHTML, current_level - 1, new_jqElem);
                }
            };

            var aside = $('#heading_list');
            if (aside.length != 0){
                var isOpen = aside.dialog('isOpen');
                aside.html('');
            } else {
                aside = $('<aside>').attr('id', 'heading_list');
                $('body').append(aside);
            }
            if (heading_list_array.length == 0) {
                aside.append('<p style="font-weight: bold;">No heading found</p><p>Create some heading for index please.</p>')
            }
            var jqElem = aside;
            var current_level = 0;

            for (var i = 0; i < heading_list_array.length; i++){
                var elem = heading_list_array[i];
                var level;
                switch(elem.tagName.toLowerCase()){
                    case 'h1':
                        level = 1;
                        break;
                    case 'h2':
                        level = 2;
                        break;
                    case 'h3':
                        level = 3;
                        break;
                    case 'h4':
                        level = 4;
                        break;
                    case 'h5':
                        level = 5;
                        break;
                    case 'h6':
                        level = 6;
                        break;
                }
                jqElem = indent(i, level, elem.innerHTML, current_level, jqElem);
                current_level = level;
            }
            return aside;
        };
        // 取得 heading list 原始資料
        var heading_list_array = $('#main_content h1, #main_content h2, #main_content h3, #main_content h4, #main_content h5, #main_content h6').map(function(index, domElement){return domElement;});
        var aside = gen_heading_list(heading_list_array);
        aside.dialog({
            title: 'Index of Content',
            stack: false,
            position: {my: 'right top', at: 'right top', of: window}
        });
    };



    // 給定 index，滾動到指定的 heading
    function scroll_to_heading(index){
        var elem = $('#main_content h1, #main_content h2, #main_content h3, #main_content h4, #main_content h5, #main_content h6').get(index);
        var jelem = $(elem);
        $('html, body').animate({
            scrollTop: jelem.offset().top
        }, 500);
    };



    window['lo']['content']['startup'] = content_startup;
    window['lo']['content']['make_record_dia'] = prompt_make_record;
    window['lo']['content']['update'] = content_update;






    // ==============================
    // edit: edit function
    //     對當前游標所在的地方，提供富文字編輯功能
    // ==============================

    window['lo']['edit'] = {};

    // 插入 HTML 原始碼
    function insert_source(){
        var sel = lo.sel.save();
        function ok(){
            var html = $(this).find('#src').val();
            if (!html){
                $(this).find('#info').html('Please input first!').css('color','red');
                return false;
            }
            $(this).dialog('close');
            lo.sel.restore(sel);
            document.execCommand("insertHTML", false, html);
            $(this).dialog('destroy');
            $('article#main_content').focus();
            return false;
        };

        var diaopt = {
            modal: true,
            width: 'auto',
            title: 'Insert Source',
            position: { my: "right top", at: "top", of: window },
            buttons: {
                "OK": ok,
                "Cancel": function(){
                    $(this).dialog( "destroy" );
                    lo.sel.restore(sel);
                    $('article#main_content').focus();
                }
            }
        };
        var dia = $('<div>').attr('title', 'Insert Source')
            .on('input', function(){var val = $(this).find('#src').val(); $(this).find('#preview').html(val);})
            .append('<p id="info">')
            .append('Source code:<br/>')
            .append('<textarea id="src" style="width: 350px; height: 100px;" placeholder="Please input any html source code in here">')
            .append('<div id="preview">')
            .keypress(function(e) {
                if (e.keyCode == $.ui.keyCode.ENTER) {
                    e.preventDefault();
                    $(':button:contains("OK")').click();
                    return false;
                }});
            //.bind('keydown',  function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});

        // 填入選擇區內容
        var clonedSelection = sel.cloneContents();
        var div = document.createElement('div');
        div.appendChild(clonedSelection);
        dia.find('#src').val(div.innerHTML);
        dia.find('#preview').html(div.innerHTML);

        dia.dialog(diaopt);
        return false;
    };



    // 變更文字顏色
    function color_change(){
        var sel = lo.sel.save();
        function ok(){
            var color = $(this).find('#used_color').val();
            var mode = $(this).find('#mode').val();
            $(this).dialog('close');
            lo.sel.restore(sel);
            document.execCommand(mode, false, color);
            localStorage['used_color'] = color;
            localStorage['used_color_mode'] = mode;
            $(this).dialog('destroy');
            $('#main_content').focus();
            return false;
        };

        var diaopt = {
            width: 'auto',
            title: 'Assign Color',
            position: { my: "center", at: "center", of: window },
            buttons: {
                "OK": ok,
                "Cancel": function(){
                    var color = $(this).find('#used_color').val();
                    localStorage['used_color'] = color;
                    $(this).dialog( "destroy" );
                    lo.sel.restore(sel);
                }}
        };
        var dia = $('<div><br/>mode:<br/><select id="mode"><option value="foreColor">foreground</option><option value="hiliteColor">background</option></select><br/><br/>color:<br/><input id="used_color" placeholder="eg: #f00, #3504f7" type="color" /><p style="font-size: x-small; color: gray;">If your browser didn\'t support color selector,<br/>you can get some color code in <a href="http://html-color-codes.info/" target="_blank">here</a>.</p></div>');
        var old_color = localStorage['used_color'];
        if (old_color == undefined || old_color == '' || old_color == null){
            old_color = '#ff6347';
        }
        dia.find('#used_color').val(old_color);

        var old_color_mode = localStorage['used_color_mode'];
        if (old_color_mode != 'hiliteColor'){
            dia.find('#mode option[value=foreColor]').attr('selected', 'selected');
        } else {
            dia.find('#mode option[value=hiliteColor]').attr('selected', 'selected');
        }

        dia.bind('keydown', function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});
        dia.dialog(diaopt);
        return false;
    };



    // 變更字體大小
    function font_size_change(){
        var sel = lo.sel.save();
        function ok(){
            var size = $(this).find('#fontsize').val();
            $(this).dialog('close');
            lo.sel.restore(sel);
            document.execCommand('fontSize', false, size);
            $(this).dialog('destroy');
            $('#main_content').focus();
            return false;
        };

        var diaopt = {
            width: 'auto',
            title: 'Font Size',
            buttons: {
                "OK": ok,
                "Cancel": function(){
                    $(this).dialog( "destroy" );
                    lo.sel.restore(sel);}}
        };
        var fly_html = '<div>size:<br/><select id="fontsize"><option value="1">1</option><option value="2">2</option><option value="3">3</option><option selected="selected" value="4">default</option><option value="5">5</option><option value="6">6</option><option value="7">7</option></select></div>'
        var dia = $(fly_html);
        dia.bind('keydown', function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});
        dia.dialog(diaopt);
        return false;
    };



    // 增大所選位置的字型
    function increase_font_size(){
        var cfs = document.queryCommandValue("fontSize");
        if(!cfs){
            document.execCommand('increaseFontSize');
        } else{
            var size = parseInt(cfs) + 1;
            if (size<=7)
                document.execCommand('fontSize', false, size);
        }
        return false;
    };



    // 減小所選位置的字型
    function decrease_font_size(){
        var cfs = document.queryCommandValue("fontSize");
        if(!cfs){
            document.execCommand('decreaseFontSize');
        } else {
            var size = parseInt(cfs) - 1;
            if (size>0)
                document.execCommand('fontSize', false, size);
        }
        return false;
    };



    // 取得現有的 time_insert_format
    function get_time_insert_format(){
        var time_insert_format = localStorage['time_insert_format'];
        if(time_insert_format == undefined || time_insert_format == null || time_insert_format == '') {
            time_insert_format = '%H:%M:%S';
            localStorage['time_insert_format'] = time_insert_format;
        }
        return time_insert_format;
    };

    

    // 插入 time_insert_format
    function time_insert(){
        var d = new Date();
        var time_insert_format = get_time_insert_format();
        document.execCommand("insertHTML", false, d.strftime(time_insert_format));
        return false;
    };



    // 取得現有的 date_insert_format
    function get_date_insert_format(){
        var date_insert_format = localStorage['date_insert_format'];
        if(date_insert_format == undefined || date_insert_format == null || date_insert_format == '') {
            date_insert_format = '%Y-%m-%d';
            localStorage['date_insert_format'] = date_insert_format;
        }
        return date_insert_format;
    };

    

    // 插入 date_insert_format
    function date_insert(){
        var d = new Date();
        var date_insert_format = get_date_insert_format();
        document.execCommand("insertHTML", false, d.strftime(date_insert_format));
        return false;
    };



    // 建立超連結
    function create_link(){
        var seltext = window.getSelection().toString();
        var at_index = seltext.lastIndexOf('@');
        if(at_index == -1){ //沒找到@
            var display = seltext;
            var link = seltext;
        } else {
            var display = seltext.substring(0, at_index);
            var link = seltext.substring(at_index+1);
        }
        //分析link，檢查link是否為有效連結
        var valid = link.match(/^(?:(?:https?|ed2k|ftp|samba|file):\/\/|magnet:\?)/i);
        if(valid){
            //如果可行，試著轉換為相對連結
            var linkparser = document.createElement('a'); //link parser
            linkparser.href = link;
            //轉換的條件為protocol一樣且host一樣
            if(location.protocol == linkparser.protocol && location.host == linkparser.host){
                var uri = new URI(link);
                link = uri.relativeTo(location.href);
            }
            var html = '<a href="' + link + '">' + display + '</a>';
            document.execCommand('insertHTML', false, html);
        }
    }



    // 建立 abbr 縮寫
    // bug when attach line start or line end in chrome!
    // function create_abbr(){
    //     var seltext = window.getSelection().toString();
    //     var at_index = seltext.lastIndexOf('@');
    //     if(at_index == -1){ //沒找到@
    //         return false;
    //     } else {
    //         var display = seltext.substring(0, at_index);
    //         var title = seltext.substring(at_index+1);
    //         var html = '<abbr title="' + title + '">' + display + '</abbr>';
    //         document.execCommand('insertHTML', false, html);
    //         //pasteHtmlAtCaret(html);
    //     }
    // }



    //建立小物件
    // bug when attach line start or line end in chrome!
    // function create_small_elem(name){
    //     var seltext = window.getSelection().toString();
    //     var html = '<' + name + '>' + seltext + '<' + name + '/>';
    //     document.execCommand('insertHTML', false, html);
    // }



    // 跳出對話框，讓使用者選擇插入額外資源
    function prompt_resource_update(){
        // 如果 include 回傳 true
        function include(arr, obj) {
            return (arr.indexOf(obj) != -1);
        }
        // 準備資料
        var sel = lo.sel.save();
        var key = 'insert_formats';
        // formats 未初始化時的初始化方法
        if(localStorage[key] == undefined || localStorage[key] == '' || localStorage[key] == null){
            var default_formats = [
                '<a href="{fpath}"><img src="{fpath}"></a>',
                '<figure><figcaption>{fpath}</figcaption><a href="{fpath}"><img src="{fpath}"></a></figure><br/>',
                '<a href="{fpath}">{fpath}<a>',
                ];
            var j_str = JSON.stringify(default_formats);
            localStorage[key] = j_str;
        }
        var j_str = localStorage[key];
        var formats = JSON.parse(j_str);

        // 定義按鈕回呼函式
        function ok(){
            // 讀取必要資料
            var type = $(this).find('#res_type').val();
            var filename = $(this).find('#uploadfile')[0].files[0].name;
            var file = $(this).find('#uploadfile')[0].files[0];
            var format = $(this).find('#format').val();
            // 記錄輸入格式
            var index = formats.indexOf(format);
            if (index != -1){ // 如果原本存在則刪掉
                formats.splice(index, 1);
            }
            formats.unshift(format); // 填入這次的新值於第一位置
            if (formats.length > 10){ // 最長為10
                formats.length = 10;
            }
            var j_str = JSON.stringify(formats);
            localStorage[key] = j_str;

            // 要插入的格式（將{fpath}替換為filename）
            var html = format.replace(/{fpath}/gi, filename);

            // 定義上傳後正確與錯誤的反應
            function error(jqXHR){return ajax_standard_error(jqXHR, 'Upload Failed');};
            function success(data){
                dia.dialog('destroy');
                lo.sel.restore(sel);
                if (html && type == 'emb')
                    document.execCommand('insertHTML', false, html);
                    //掛載圖片滾輪縮放功能
                    lo.key.img_ready();
                $('#main_content').focus();
            };
            $.ajax({
                url: filename + '?res_type=' + type,
                type: 'PUT',
                data: file,
                success: success,
                error: error,
                cache: false,
                contentType: false,
                processData: false
            });
            return false;
        };

        // 對話框組態
        var fly_html = '<div><p><input id="uploadfile" type="file" /></p><p>type:<br/><select id="res_type"><option value="emb">⎚ Embedded</option><option value="att">✉ Attachment</option></select></p><p>Insert format:<br/><input id="format" type="text" placeholder="{fpath} == filepath" style="width:100%;" /></p><p><select id="formats" style="width:100%;" ></select></p></div>'
        var diaopt = {
            width: 'auto',
            title: 'Insert a Resource',
            modal: true,
            buttons: {
                "OK": ok,
                "Cancel": function(){
                    $(this).dialog( "destroy" );
                    lo.sel.restore(sel);}}
        };
        var dia = $(fly_html);

        // 填入之前記錄的格式
        var select_obj = dia.find('select#formats');
        for (var i = 0; i < formats.length; i++){
            var option = $('<option></option>').text(formats[i]).val(formats[i]);
            select_obj.append(option);
        }
        var format_obj = dia.find('input#format');
        format_obj.attr('value', formats[0]);

        // 掛載功能
        select_obj.on('change', function(){format_obj.val(select_obj.val());});
        dia.bind('keydown', function(e) { if (e.keyCode == $.ui.keyCode.ENTER) { e.preventDefault(); $(':button:contains("OK")').click(); return false;}});
        dia.find('#res_type').bind('change',
            function(e) {
                e.preventDefault();
                if (dia.find('#res_type').val() == 'emb'){
                    format_obj.removeAttr('disabled');
                    select_obj.removeAttr('disabled');
                }else{
                    format_obj.attr('disabled', 'disabled');
                    select_obj.attr('disabled', 'disabled');
                }
                return false;
            }
        );
        dia.dialog(diaopt);
        dia.find('#uploadfile').click(); // 點擊按鈕載入檔案
        return false;
    };



    window['lo']['get'] = {};
    window['lo']['get']['time_insert_format'] = get_time_insert_format;
    window['lo']['get']['date_insert_format'] = get_date_insert_format;






    // ==============================
    // sidebar: sidebar function
    //     提供 aside#sidebar 所需要的功能
    //     頁面中必須要有 aside#sidebar 才能正常運作
    // ==============================

    window['lo']['sidebar'] = {};

    // 讓導航選單內部能上下滾動
    function init_sidebar_wheel(){

        // 上下滾動導航項目
        function sidebar_wheel(e){
            var scroll = get_mouse_scroll(e);
            var delta = scroll*40;
            //本次偏移量計算完畢，開始計算最大偏移值
            var parentHeight = this.parentNode.offsetHeight; //父元素的高度
            var minimum = -this.offsetHeight + parentHeight;
            var maximum = 0;
            function fixedRange(val){
                if(val < minimum)
                    val = minimum;
                else if(val > maximum)
                    val = maximum;
                return val;
            }
            //開始更新資料……
            var old_offset = parseInt(this.style.top.split('px')[0]);
            if (isNaN(old_offset)){ //尚未移動過
                this.style.top = fixedRange(delta) + 'px';
            }else{
                this.style.top = fixedRange(old_offset + delta) + 'px';
            }
            //阻止事件傳播
            return stopEvent(e);
        };

        $('aside#sidebar ul:not(.menuroot)').each(
            function (index, elem){
                if($(elem).attr('class') != 'menuroot'){
                    elem.addEventListener('DOMMouseScroll', sidebar_wheel, false); //Fx
                    elem.addEventListener('mousewheel', sidebar_wheel, false); //chrome
                }
            }
        );
    };



    // 將 edit/view mode 功能掛載到側欄的按鈕上
    // 頁面需有以下 DOM 才能正常運作:
    //      #content-mode
    //      #article#main_content
    function init_editmode(){
        var sel = lo.sel.save();

        function edit_enable(){
            lo.sel.restore(sel);
            $('article#main_content')
                .attr('contenteditable', 'true')
                .focus();
            $('#content-mode')
                .html('Edit-Mode<span class="notify"><img class="icon" src="/static/icon/sidebar/editmode.svg"></span>')
                .attr('title', 'Edit-Mode (toggle by click)');
            localStorage['editmode'] = 'true';
        };
        function edit_disable(){
            sel = lo.sel.save();
            $('article#main_content')
                .attr('contenteditable', 'false')
                .blur();
            $('#content-mode')
                .html('View-Mode<span class="notify"><img class="icon" src="/static/icon/sidebar/viewmode.svg"></span>')
                .attr('title', 'View-Mode (toggle by click)');
            localStorage['editmode'] = 'false';
        };
        function editfirst(editnow){
            shortcut.add("F10",function() {
                $('#content-mode').click();
            });

            var editnow = editnow;
            function mode_toggle(){
                if(editnow){
                    editnow = !editnow;
                    return edit_disable();
                } else {
                    editnow = !editnow;
                    return edit_enable();
                }
            }

            if (editnow){ edit_enable(); }
            else { edit_disable(); }
            $('#content-mode').on('click', mode_toggle);
        };

        if(localStorage['editmode'] == undefined){
            editfirst(true);
        }else{
            if(localStorage['editmode'] == 'true'){
                editfirst(true);
            }
            else{
                editfirst(false);
            }
        }
    };


    /*
    // 拖放支援
    function show_sidebar_item_menu(e){
        $(this).find('> ul').addClass('sidebar-menu-show');
        e.stopPropagation();
    }



    // 拖放支援
    function hide_sidebar_item_menu(e){
        $(this).find('> ul').removeClass('sidebar-menu-show');
    }
    */


    // 初始化 sidebar 的所有功能。
    // 畫面上當然必須要有 aside#sidebar...
    function sidebar_ready() {
        $('aside#sidebar').ready(function(){
                $('#btn_note_create').on('click', lo.noteop.create_dia);
                $('#btn_title_update').on('click', lo.noteop.rename_dia);
                $('#btn_note_delete').on('click', lo.noteop.del_dia);
                $('#btn_content_update').on('click', lo.content.update);
                $('#btn_make_record').on('click', lo.content.make_record_dia);
                $('#btn_position_update').on('click', lo.noteop.pos_dia);
                $('#btn_note_export').on('click', lo.noteop.export_dia);
                $('#btn_book_import').on('click', lo.noteop.import_dia);
                lo.sidebar.ev_mode_toggle_ready();
                // $('#sidebar li').hover(
                //     show_sidebar_item_menu, hide_sidebar_item_menu);
                // $('#sidebar li').bind('dragover', show_sidebar_item_menu)
                //     .bind('dragleave', hide_sidebar_item_menu);
                lo.sidebar.wheel_ready();
            }
        );
    };



    window['lo']['sidebar']['wheel_ready'] = init_sidebar_wheel;
    window['lo']['sidebar']['ev_mode_toggle_ready'] = init_editmode;
    window['lo']['sidebar']['ready'] = sidebar_ready;






    // ==============================
    // key: hotkey function
    //     將功能掛載到熱鍵，供使用者使用的函式組
    //     如何掛載，熱鍵為何，都由個別項目的 ready 函式決定
    // ==============================

    window['lo']['key'] = {};

    // 替 img 掛上滾輪縮放功能
    function img_ready(){
        var root_selector = 'article#main_content';
        $(root_selector).find('img').each(
            function (index, elem){
                elem.addEventListener('DOMMouseScroll', mouseWheelResize, false); //Fx
                elem.addEventListener('mousewheel', mouseWheelResize, false); //chrome
                elem.addEventListener('click', setDefaultSize, false);
            }
        );
    }



    // 啟動搜尋功能
    function search_ready(){
        shortcut.add("F4", function(){
            prompt_search();
        });
    }



    // 啟用筆記 noteop 編輯功能
    // 含更名、位置變更、新建筆記、刪除筆記等的熱鍵
    function noteop_ready(){
        shortcut.add("F7", function(){
            prompt_position_update();
        });
        shortcut.add("Shift+Delete", function(){
            confirm_note_delete();
        });
        // chrome按ctrl+n會跳出新視窗，所以廢棄改為 Alt + N
        // shortcut.add("Ctrl+N",function(){
        //     prompt_note_create();
        // });
        shortcut.add("Alt+N",function(){
            prompt_note_create();
        });
        shortcut.add("F2",function(){
            prompt_title_update();
        });
        shortcut.add("F8",function(){
            prompt_note_export();
        });
        shortcut.add("F9",function(){
            prompt_book_import();
        });
    };



    // 啟用與 content 有關的功能
    function content_ready(){
        shortcut.add("Ctrl+Alt+N",function(){
            prompt_make_record();
        });
        shortcut.add("Alt+Q",function(){
            show_heading_list();
        });
    };



    // 啟用 rich text 編輯用熱鍵
    function edit_ready(){
        // 移除格式
        shortcut.add("Ctrl+K",function() {
            document.execCommand("removeFormat");
        });
        shortcut.add("Ctrl+Shift+K",function() {
            document.execCommand("unlink");
        });
        // 基本格式
        shortcut.add("Ctrl+B",function() {
            document.execCommand("bold");
        });
        shortcut.add("Ctrl+I",function() {
            document.execCommand("italic");
        });
        shortcut.add("Ctrl+U",function() {
            document.execCommand("underline");
        });
        shortcut.add("Ctrl+S",function() {
            document.execCommand("strikethrough");
        });
        // shortcut.add("Alt+O",function() {
        //     font_size_change();
        // });
        shortcut.add("Alt+Up",function() {
            increase_font_size();
        });
        shortcut.add("Alt+Down",function() {
            decrease_font_size();
        });
        shortcut.add("Ctrl+E",function() {
            color_change();
        });
        // 上下標
        shortcut.add("Alt+K",function() {
            document.execCommand("superscript");
        });
        shortcut.add("Alt+J",function() {
            document.execCommand("subscript");
        });
        // 段落標記
        shortcut.add("Alt+0",function() {
            document.execCommand("formatblock", false,"p");
        });
        shortcut.add("Alt+`",function() {
            document.execCommand("formatblock", false,"p");
        });
        shortcut.add("Alt+1",function() {
            document.execCommand("formatblock", false,"h1");
        });
        shortcut.add("Alt+2",function() {
            document.execCommand("formatblock", false,"h2");
        });
        shortcut.add("Alt+3",function() {
            document.execCommand("formatblock", false,"h3");
        });
        shortcut.add("Alt+4",function() {
            document.execCommand("formatblock", false,"h4");
        });
        shortcut.add("Alt+5",function() {
            document.execCommand("formatblock", false,"h5");
        });
        shortcut.add("Alt+6",function() {
            document.execCommand("formatblock", false,"h6");
        });
        shortcut.add("Alt+7",function() {
            document.execCommand("formatblock", false,"blockquote");
        });
        shortcut.add("Alt+8",function() {
            document.execCommand("formatblock", false,"pre");
        });
        // shortcut.add("Alt+9",function() {
        //     document.execCommand("formatblock", false,"dt");
        // });
        // 左右對齊
        shortcut.add("Alt+,",function() {
            document.execCommand("JustifyLeft");
        });
        shortcut.add("Alt+.",function() {
            document.execCommand("JustifyRight");
        });
        shortcut.add("Alt+M",function() {
            document.execCommand("JustifyCenter");
        });
        // 縮進
        shortcut.add("Alt+",function() {
            document.execCommand("Indent");
        }, { 'keycode':221 }); //右方括號的keycode
        shortcut.add("Alt+",function() {
            document.execCommand("Outdent");
        }, { 'keycode':219 }); //左方括號的keycode
        // 列表
        shortcut.add("Alt+P",function() {
            document.execCommand("InsertUnorderedList");
        });
        shortcut.add("Alt+O",function() {
            document.execCommand("InsertOrderedList");
        });
        // 插入原始碼
        shortcut.add("Ctrl+Alt+I",function() {
            insert_source();
        });
        // 插入超連結
        shortcut.add("Ctrl+L",function() {
            create_link();
        });
        // 插入 abbr 縮寫標籤
        // shortcut.add("Alt+A",function() {
        //     create_abbr();
        // });
        // 插入 code 縮寫標籤
        // shortcut.add("Alt+C",function() {
        //     create_small_elem('kdb');
        // });
        // 插入資源
        shortcut.add("Alt+I",function() {
            prompt_resource_update();
        });
        shortcut.add("Alt+T",function() {
            time_insert();
        });
        shortcut.add("Alt+D",function() {
            date_insert();
        });

    };



    window['lo']['key']['img_ready'] = img_ready;
    window['lo']['key']['search_ready'] = search_ready;
    window['lo']['key']['noteop_ready'] = noteop_ready;
    window['lo']['key']['content_ready'] = content_ready;
    window['lo']['key']['edit_ready'] = edit_ready;






})();
